<?php
/**
 * 核心类库
 * @copyright reginx.com
 * $Id: core.class.php 1473 2012-10-09 13:01:01Z lan7 $
 */
class core{

    /**
     * 应用信息
     *
     * @var unknown_type
     */
    private $_app  = array();

    /**
     * 配置
     *
     * @var unknown_type
     */
    private $_conf = array();

    /**
     * 构造函数
     *
     */
    public function __construct($appfile,$conf){

        /**
         * 关闭自动转义
         */
        PHP_VERSION < '5.3.0' && set_magic_quotes_runtime(0);

        /**
         * 安全过滤
         */
        if (isset($_GET['GLOBALS']) || isset($_POST['GLOBALS']) ||  isset($_COOKIE['GLOBALS']) || isset($_FILES['GLOBALS'])) {
            self::error(self::L(('request-tainting')),'Sys::initInput',1);
        }

        /**
         * 过滤非法全局变量
         */
        if (isset($_GET['GLOBALS']) || isset($_POST['GLOBALS']) || isset($_COOKIE['GLOBALS']) || isset($_FILES['GLOBALS'])){
            self::error(core::L('request-tainting') , 'Sys::initInput' , 1);
        }
        $sglobal = array('GLOBALS','_GET','_POST','_REQUEST','_COOKIE','_SERVER','_ENV','_FILES','_MISC');
        foreach ($GLOBALS as $key => $value) {
            if (!in_array($key , $sglobal)) {
                $GLOBALS[$key] = null; unset($GLOBALS[$key]);
            }
        }

        /**
         * 还原数据 , 如果被转义了.
         */
        if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()){
            $_GET       = self::dstripslashes($_GET);
            $_POST      = self::dstripslashes($_POST);
            $_COOKIE    = self::dstripslashes($_COOKIE);
        }

        /**
         * 默认配置
         */
        $this->_conf = $conf;

        /**
         * 加载app配置信息
         */
        if(file_exists(APP_PATH . 'config/config.php')){
            $this->_conf = array_merge($this->_conf , include_once(APP_PATH . 'config/config.php'));
        }

        /**
         * 生成当前配置
         */
        $GLOBALS['_APP'] = $this->_app  = $this->_getapp($appfile);

        /**
         * 设置时区
         */
        $this->timezone_set();

        /**
         * 加载语言变量
         */
        $GLOBALS['_LANG'] = lang::load($this->_conf['lang']);

        /**
         * 模板操作对象
         */
        $GLOBALS['_TPL']  = tpl::getTpl($GLOBALS['_APP']);

        /**
         * 缓存对象
         */
        $GLOBALS['_CACHE'] = cache_extra::getCacheObj($GLOBALS['_APP']);

        /**
         * 解析url
         */
        $this->_parseurl();
    }


    /**
     * 设置时区
     *
     * @param unknown_type $timeoffset
     */
    public function timezone_set($timeoffset = 0) {
        if(function_exists('date_default_timezone_set')) {
            date_default_timezone_set('Etc/GMT' . ($this->_conf['time_offset'] > 0 ? '-' : '+') . (abs($timeoffset)));
        }
        define('REQUEST_TIME' , time());
    }



    /**
     * 检测是否为爬虫
     * @from x25
     * @param mixed $ua
     * @return boolean
     */
    public function isrobot($ua = '') {
        static $spiders = array('bot', 'crawl', 'spider' ,'slurp', 'sohu-search', 'lycos', 'robozilla');
        static $browsers = array('msie', 'netscape', 'opera', 'konqueror', 'mozilla');
        $ua   = strtolower(empty($ua) ? $_SERVER['HTTP_USER_AGENT'] : $ua);
        if(strpos($ua, 'http://') === false && self::dstrpos($ua , $browsers)){
            return false;
        }
        if(self::dstrpos($ua, $spiders)){
            return true;
        }
        return false;
    }


    /**
     * strpos 加强版 ...
     *
     * @param string $string
     * @param array $arr
     * @param mixed $ret
     * @return mixed
     */
    public static function dstrpos($string, &$arr, $ret = false) {
        if(empty($string)){
            return false;
        }
        foreach((array)$arr as $v) {
            if(strpos($string, $v) !== false) {
                return $ret ? $v : true;
            }
        }
        return false;
    }
    /**
     * 获取应用信息
     *
     * @param unknown_type $appfile
     * @return unknown
     */
    private function _getapp($appfile){
        // 入口文件
        $appfile = defined('APP_FILE') ? APP_FILE : $appfile;
        $ret = array(
        'app_path'      => APP_PATH,
        'app_name'      => basename(dirname($appfile)),
        'app_file'      => basename($appfile),
        'app_dir'       => $this->_getappdir(),
        'app_tpl_path'  => APP_PATH  . 'template/',
        'base_path'     => BASE_PATH,
        'data_path'     => BASE_PATH . 'data/',
        'inc_path'      => BASE_PATH . 'include/',
        'temp_path'     => BASE_PATH . 'data/temp/',
        'cache_path'    => BASE_PATH . 'data/cache/',
        'reginx_path'   => REGINX_PATH ,
        'extra_path'    => REGINX_PATH.'extra/',
        );
        $ret['base_url']    = $this->_getbaseurl($ret['app_name'],$ret['app_dir']);
        $ret['data_url']    = $ret['base_url'] . 'data/';
        $ret['upload_url']  = $ret['data_url'] . 'attachment/';
        $ret['app_url']     = $ret['base_url'] . $ret['app_dir'];
        $ret['curtpl_url']  = $ret['app_url']  . 'template/' . $this->_conf['tpl'] .'/';
        $ret['is_robot']    = $this->isrobot();
        foreach ($ret as $k => $v){
            define(strtoupper($k) , $v);
        }
        return array_merge($this->_conf,$ret);
    }

    /**
     * 解析生成缩略图对应的url
     *
     * @param unknown_type $image
     * @return unknown
     */
    public static function thumburl($image){
        $tmp = explode('.' , $image);
        return $tmp[0] . "_thumb." . $tmp[1];
    }

    /**
     * 删除目录
     *
     * @param String $path
     * @param Boolean $sub 是否仅删除子目录
     */
    public static function removeDir($path , $sub = FALSE){
        $path = realpath($path);
        if(preg_match('/(temp|cache)/i',$path) && is_dir($path)){
            foreach(glob($path . "/*") as $file){
                if(is_file($file) === TRUE){
                    unlink($file);
                }else if (is_dir($file) === TRUE){
                    self::removeDir($file);
                }
            }
            !$sub && rmdir($path);
        }else{
            self::error(self::L('delete-denied'),'core::rmdir');
        }
    }

    /**
     * 反引用字符串支持数组
     *
     * @param unknown_type $str
     * @return unknown
     */
    public static function dstripslashes($str) {
        if(is_array($str)) {
            foreach($str as $k => $v) {
                $str[$k] = self::dstripslashes($v);
            }
        }else{
            $str = stripslashes($str);
        }
        return $str;
    }

    /**
     * 获取站点Url
     *
     * @param unknown_type $name
     * @param unknown_type $dir
     * @return unknown
     */
    private function _getbaseurl($name,$dir){
        $sfile = strtolower("http://{$_SERVER['HTTP_HOST']}{$_SERVER['SCRIPT_NAME']}");
        $name  = $name == basename($sfile,'.php') ? $name : basename($sfile,'.php');
        $tmp   = explode($name.'.php',$sfile);
        return str_replace($dir,'',array_shift($tmp));
    }

    /**
     * 获取lib tab 对象
     *
     * @param unknown_type $class
     * @param unknown_type $single
     * @return unknown
     */
    public static function getobj($class , $single = true){
        static $objbus = array();
        if($single){
            if(!isset($objbus[$class])){
                $objbus[$class] = new $class();
            }
            return $objbus[$class];
        }else{
            return new $class();
        }
    }

    /**
     * 获取应用目录
     *
     * @return unknown
     */
    private function _getappdir(){
        $ret = str_replace(realpath(BASE_PATH),'',realpath(APP_PATH));
        return substr($ret , 1) . '/';
    }

    /**
     * autoload
     *
     * @param unknown_type $class
     */
    public static function loader($class){
        if (!class_exists($class, false)){
            $class = explode("_",$class);
            $ctype = array_pop($class);
            $class = $ctype.DIR_SEP.implode(DIR_SEP,$class);
            $path  = isset($GLOBALS['_APP']['main']) && $GLOBALS['_APP']['main'] ? BASE_PATH : APP_PATH;
            $path = in_array($ctype,array('tab','lib')) ? BASE_PATH : $path;
            $file  = $path . 'include'. DIR_SEP . "{$class}.{$ctype}.php";
            if(!is_file($file)){
                if($ctype == 'mod' && !IS_DEBUG){
                    core::getobj('mod')->show404();
                }else{
                    self::error(self::L('not-found', $class.".$ctype.php" ),'core::loader',1);
                }
            }else{
                include_once($file);
            }
        }
    }

    /**
     *  输出异常信息
     *
     * @param String $msg
     * @param String $from
     * @param Integer $level
     */
    public static function error($msg , $from = 'Sys',$log = 0 , $extra=''){
        $log && self::log($msg . ($extra ? (' extra : ' . $extra ): '') , $from);
        ob_end_clean();
        header('X-Powered-By: REGINX v'.REGINX_VERSION);
        $temp = '<!DOCTYPE html><html><head><title>reginx error </title>';
        $temp.= '<meta http-equiv="content-type"  content="text/html; charset='.$GLOBALS['_APP']['charset'].'"/>';
        $temp.= '<meta name="ROBOTS" content="NOINDEX,NOFOLLOW,NOARCHIVE" /><style type="text/css">body { background-color:';
        $temp.= ' white; color: black;font: 12px verdana, arial, sans-serif;} #container { width: 700px;} #message {background-color:';
        $temp.= ' #FFFFCC; } #bodytitle {vertical-align: top; } .bodytext { } b{font-weight:normal}';
        $temp.= ' .help  {font-size:12px; color: red;} .red {color: red;} .trace{font-size:11px;}';
        $temp.= ' a:link { font-size:12px; color: red; } a:visited{font-size:11px; color: #4e4e4e; ';
        $temp.= '} </style></head><body><table cellpadding="1" cellspacing="5" ';
        $temp.= 'id="container"><tr><td class="bodytext"><br/>error';
        $temp.= ' messages: </td></tr><tr><td class="bodytext" id="message"><ul><li>'.(is_array($msg)?explode('<br/>',$msg):$msg);
        $temp.= ' &nbsp;&nbsp;<a href="http://www.reginx.com/help/error/'.base64_encode($msg).'" target="_blank"';
        $temp.= ' style="color:blue;text-decoration:none">'.self::L('get-help').'</a></li></ul></td></tr>';
        $temp.= '<TRACE/>';
        $temp .= '<tr><td class="help"><a href="'.$GLOBALS['_APP']['app_url'].'">'.$_SERVER['HTTP_HOST'].'</a> ';
        $temp .= self::L('err-recorded').' </td></tr></table></body></html>';
        if(IS_DEBUG){
            $trace = debug_backtrace();
            $str = '<tr><td class="bodytext"><br/>trace information: <br/><ul class="trace">';
            for ($i = 0; $i<sizeof($trace); $i++){
                $str .= '<li>[ #'.$i.' line:'.sprintf('%04d' , empty($trace[$i]['line']) ? 0 : $trace[$i]['line']).' ] ';
                $str .= ' File: '.str_replace( BASE_PATH ,'' , empty($trace[$i]['file']) ? '': $trace[$i]['file']);
                $str .= ' '.(empty($trace[$i]['class']) ? '' : $trace[$i]['class']);
                $str .= (empty($trace[$i]['type']) ? ''      : $trace[$i]['type']);
                $str .= (empty($trace[$i]['function']) ? ''  : $trace[$i]['function']) . '()</li>';
            }
            $temp = str_replace('<TRACE/>',$str.'</ul></td></tr>',$temp);
        }
        echo($temp);
        $temp = null; unset($temp);
        exit();
    }

    /**
     * 语言
     *
     * @return unknown
     */
    public static function L(){
        return $GLOBALS['_TPL']->lang(func_get_args());
    }

    /**
     * 记录日志
     *
     * @param String $msg
     */
    public static function log($msg , $from=''){
        !is_dir($GLOBALS['_APP']['data_path']    . 'log/') && mkdir($GLOBALS['_APP']['data_path'].'log/');
        !is_dir($GLOBALS['_APP']['data_path']    . 'log/'  . $GLOBALS['_APP']['app_dir']) && mkdir($GLOBALS['_APP']['data_path'].'log/'.$GLOBALS['_APP']['app_dir']);
        $logfile = $GLOBALS['_APP']['data_path'] . 'log/'  . $GLOBALS['_APP']['app_dir'] . (date('Y-m-d',REQUEST_TIME)).'.log.php';
        $from    = $GLOBALS['_APP']['app_dir'] . (!empty($form) ? $form : '');
        $msg = '<?php exit();?> '.$from.' -> '.self::getip().': '.(date('Y-m-d H:i:s ', REQUEST_TIME)).$_SERVER['PHP_SELF'].' '.$msg." \n";
        $fp  = fopen($logfile,'a+');
        fwrite($fp,$msg);
        fclose($fp);
    }

    /**
     * 获取IP
     *
     * @return unknown
     */
    public static function getip(){
        $ip = "unkown";
        if(isset($_SERVER)) {
            if(isset($_SERVER["HTTP_X_FORWARDED_FOR"])){
                $ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
            }elseif(isset($_SERVER["HTTP_CLIENT_IP"])){
                $ip = $_SERVER["HTTP_CLIENT_IP"];
            }else{
                $ip = $_SERVER["REMOTE_ADDR"];
            }
        }
        return $ip;
    }

    /**
     * 解析url(参数)
     *
     * @param unknown_type $url
     * @return unknown
     */
    private function _parseurl(){
        $mod = $act = $tmp = NULL;
        switch ($this->_conf['url']['type']){
            case 0:
                $GLOBALS['_MOD'] = (empty($_GET['m'])||$_GET['m'] == '') ? $this->_conf['default_mod'] : $_GET['m'];
                $GLOBALS['_ACT'] = (empty($_GET['a'])||$_GET['a'] == '') ? $this->_conf['default_act'] : $_GET['a'];
                $GLOBALS['_RG']  = &$_GET;
                return ;
                break;
            case 1:
                // http://reginx.com/?index/index.html
                $query = $_SERVER['QUERY_STRING'];
                //兼容iis
                if(isset($_SERVER['HTTP_X_REWRITE_URL'])){
                    $query = explode('?',$_SERVER['HTTP_X_REWRITE_URL']);
                    $query = $query[1];
                }
                $tmp = $query != ''? explode($this->_conf['url']['interval'],str_replace($this->_conf['url']['suf'],'',$query)):array();
                break;
            case 2:
                // http://reginx.com/index.php/index/index.html
                if(isset($_SERVER['PATH_INFO']) && !empty($_SERVER['PATH_INFO']) && $_SERVER['PATH_INFO'] != "/"){
                    // 兼容Nginx模拟的PATH_INFO
                    if(substr($_SERVER['PATH_INFO'],-1) == '/'){
                        $_SERVER['PATH_INFO'] = substr($_SERVER['PATH_INFO'],0,-1);
                    }
                    //去除第一个/,url后缀.然后分割
                    $tmp = explode($this->_conf['url']["interval"],str_replace($this->_conf['url']["suf"],'',substr($_SERVER['PATH_INFO'],1)));
                }
                break;
        }

        $GLOBALS['_MOD'] = $this->_conf['default_mod'];
        if((!empty($tmp[0]) && !$tmp[0] == '' && preg_match('/^\w+$/i' , $tmp[0]))){
            $GLOBALS['_MOD'] = array_shift($tmp);
        }

        $GLOBALS['_ACT'] = $this->_conf['default_act'];
        // 匿名Action , 兼容中文
        //if((!empty($tmp[0]) && !$tmp[0] == '' && preg_match('/^\w+$/i' , $tmp[0]))){
        if((!empty($tmp[0]) && !$tmp[0] == '')){
            $GLOBALS['_ACT'] = array_shift($tmp);
        }

        for ($i=0;$i<sizeof($tmp);$i+=2){
            $GLOBALS['_RP'][$tmp[$i]] = $tmp[$i+1];
        }

    }

    /**
     * 创建文件夹
     *
     * @param String $dir
     */
    public static function makedir($dir){
        if(!is_dir($dir)){
            mkdir($dir,0755) && file_put_contents($dir.'/index.html',' ');
        }
    }

    /**
     * url 构造器
     * @example url("index-news-cat_id-10-id-%d|true",2)
     *             http://xx.com/index.php?mod=index&act=news&cat_id=10&id=2 (兼容模式)
     *             http://xx.com/index.php/index/news/cat_id_10/id_2.shtml (path_info)
     *             http://xx.com/index/news/cat_id_10/id_2.shtml (须服务器支持htaccess)
     *             http://xx.com/?index/news/cat_id_10/id_2.shtml (伪静态模式)
     *             具体转换结果,以配置文件设置为准
     * 注: 在开启mk_html的情况下,该函数会判断缓存html是否可用
     *        可用情况下会返回html的url,否则返回 php的url
     *
     * @param string $param
     * @return string
     */
    static function url($param){
        $url    = '';
        $urlSuf = $GLOBALS['_APP']['url']['suf'];
        $params = func_get_args();
        $urlStr = array_shift($params);
        $pos    = strpos($urlStr,'@');
        $app    = $GLOBALS['_APP']['app_url'];
        if($pos !== FALSE){
            $app = $GLOBALS['_APP']['base_url'];
            $app .= $pos ? (substr($urlStr,0,$pos).'/') : '';
            $urlStr = substr($urlStr,$pos+1,strlen($urlStr));
        }
        // 判断是否不需要添加 url suffer , 默认添加
        if(preg_match('/\|\s*?false/i',$urlStr)){
            $urlStr = array_shift(explode("|",$urlStr));
            $urlSuf = '';
        }
        // 参数替换
        $param =  explode("-",empty($params)?$urlStr:vsprintf($urlStr,$params));
        if(empty($param)){
            return $GLOBALS['_APP']['app_url'];
        }
        // 添加默认Grp,Mod,Act
        for($i=0; $i<2; $i++){
            if(sizeof($param)<2){
                $param[] = $GLOBALS['_APP']['default_act'];
            }
        }
        $tmpmod = array_shift($param);
        $tmpmod = $tmpmod ? $tmpmod : $GLOBALS['_APP']['default_mod'];
        $tmpact = array_shift($param);
        $tmpact = $tmpact ? $tmpact : $GLOBALS['_APP']['default_act'];
        switch (intval($GLOBALS['_APP']['url']['type'])){
            case 0:    // 普通模式
            $url = 'm='.($tmpmod ? $tmpmod : $GLOBALS['_APP']['default_mod']).'&a='.($tmpact ? $tmpact : $GLOBALS['_APP']['default_act']);
            if(!empty($param)){
                for ($i=0;$i<sizeof($param);$i+=2){
                    $url .= '&'.$param[$i].'='.$param[$i+1];
                }
            }
            return  $app . '?' . $url;
            break;

            case 1:    // 伪静态
            $url = $tmpmod . $GLOBALS['_APP']['url']['interval'] . $tmpact;
            for($j=0;$j<sizeof($param);$j+=1){
                $url .= $GLOBALS['_APP']['url']['interval'].trim($param[$j]);
            }
            return  $app . '?' . $url . $urlSuf;
            break;

            case 2:    // PATH_INFO
            $url = $tmpmod . $GLOBALS['_APP']['url']['interval'] . $tmpact;
            for($j=0; $j < sizeof($param); $j+=1){
                $url .= $GLOBALS['_APP']['url']['interval'].trim($param[$j]);
            }
            $url = $GLOBALS['_APP']['url']['rewrite'] ? $url : ('index.php/' . $url);
            return $app . $url . $urlSuf;
            break;
        }
    }

    /**
     * 获取 GPC 内容
     *
     * @param unknown_type $k
     * @param unknown_type $type
     * @return unknown
     */
    public static function getgpc($k, $type='GP') {
        switch(strtoupper($type)) {
            // GET
            case 'G': $var = &$_GET;          break;
            // POST
            case 'P': $var = &$_POST;         break;
            // COOKIE
            case 'C': $var = &$_COOKIE;       break;
            // REGINX ROUTE PARAM
            case 'R': $var = &$GLOBALS['_RP']; break;
            // DEFAULT
            default:
                $var = &$_POST;
                if(isset($_GET[$k])){
                    $var = &$_GET;
                }
                break;
        }
        return isset($var[$k]) ? $var[$k] : null;
    }



    /**
     * 是否存在GPCR值
     *
     * @param unknown_type $k
     * @param unknown_type $type
     * @return unknown
     */
    public static function hasgpc($k, $type='GP') {
        switch(strtoupper($type)) {
            // GET
            case 'G': $var = &$_GET;          break;
            // POST
            case 'P': $var = &$_POST;         break;
            // COOKIE
            case 'C': $var = &$_COOKIE;       break;
            // REGINX ROUTE PARAM
            case 'R': $var = &$GLOBALS['_RP']; break;
            // DEFAULT
            default:
                $var = &$_POST;
                if(isset($_GET[$k])){
                    $var = &$_GET;
                }
                break;
        }
        return isset($var[$k]) ? true : false;
    }


    /**
     * 获取大小
     *
     * @param Mixed $val
     * @return Integer
     */
    public static function byteslen($val) {
        $val = trim($val);
        $last = strtolower($val{strlen($val)-1});
        switch($last) {
            case 'g': $val *= 1024;
            case 'm': $val *= 1024;
            case 'k': $val *= 1024;
        }
        return $val;
    }


    /**
     * 验证令牌 
     *
     * @return unknown
     */
    public static function vtoken(){
        if(!$GLOBALS['_CFG']['app']['token']){ return TRUE;}
        $tname = $GLOBALS['_CFG']['app']['token_var'];
        if(!isset($_SESSION[$tname])){
            session_start();
            $_SESSION[$tname] = isset($_SESSION[$tname])?$_SESSION[$tname]:NULL;
        }
        return $_SESSION[$tname] == $_POST[$tname] && !empty($_SESSION[$tname]);
    }

    /**
     * 输出令牌
     *
     */
    public static function token($get = false){
        if($GLOBALS['_APP']['token']){
            $key  = $GLOBALS['_APP']['token_var'];
            $sobj = sess_extra::getobj($GLOBALS['_APP']['sess']);
            $token = $sobj->get($key);
            if($token == null){
                $token = sha1($GLOBALS['_MISC']['STIME'] . $GLOBALS['_APP']['app_name']);
                $sobj->set($key , $token);
            }
            return  $get ? $token : '<input type="hidden" name="'.$key.'" id="'.$key.'" value = "'.$token.'" />';
        }
        return null;
    }

    /**
     * 获取随机字串
     *
     * @param int $len
     * @return string
     */
    public static function randstr($len = 4){
        $ret = '';
        $randstr = '23456789abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVW';
        for($i = 0 ; $i < $len ; $i++){
            $ret .= $randstr[mt_rand(0 , strlen($randstr) - 1)];
        }
        return $ret;
    }


    /**
     * 获取当前 Url 的路由字串
     *
     * @param unknown_type $url
     * @return unknown
     */
    public static function geturl(){
        $temp = array();
        $temp[] = $GLOBALS['_MOD'];
        $temp[] = $GLOBALS['_ACT'];
        if(isset($GLOBALS['_RP']) && !empty($GLOBALS['_RG'])){
            foreach ($GLOBALS['_RP'] as $k=>$v){
                $temp[] = $k;
                $temp[] = $v;
            }
        }
        return $GLOBALS['_APP']['app_name'].'@'.implode('-' , $temp);
    }
    
    public static function cleartpl($app = ''){
        $dir = DATA_PATH . 'temp/' . $app;
        if(is_dir($dir)){
            self::removeDir($dir , 1);
        }
    }
}
?>